package com.sh.rbac.core.storage.config;


import com.sh.rbac.core.common.exception.CustomException;
import com.sh.rbac.core.common.result.ResultCodeEnum;
import com.sh.rbac.core.storage.model.UploadResult;
import com.sh.rbac.domain.entity.FileConfig;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.core.ResponseInputStream;
import software.amazon.awssdk.core.async.AsyncResponseTransformer;
import software.amazon.awssdk.core.async.BlockingInputStreamAsyncRequestBody;
import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3AsyncClient;
import software.amazon.awssdk.services.s3.S3Configuration;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.transfer.s3.S3TransferManager;
import software.amazon.awssdk.transfer.s3.model.CompletedUpload;
import software.amazon.awssdk.transfer.s3.model.Download;
import software.amazon.awssdk.transfer.s3.model.DownloadRequest;
import software.amazon.awssdk.transfer.s3.model.Upload;
import software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener;

import java.io.InputStream;
import java.net.URI;
import java.time.Duration;

/**
 * 对象存储客户端
 * 仅支持 S3 兼容的云厂商
 *
 * @author wb
 * @since 2025-10-15
 */
public class OssClient {

    /**
     * 文件配置
     */
    private final FileConfig fileConfig;

    /**
     * AWS S3 异步客户端
     */
    private final S3AsyncClient s3Client;

    /**
     * AWS S3 传输管理器
     */
    private final S3TransferManager s3TransferManager;

    /**
     * 预签名URL生成器
     */
    private final S3Presigner s3Presigner;

    public OssClient(FileConfig fileConfig) {
        this.fileConfig = fileConfig;

        // 创建 AWS 凭证
        AwsBasicCredentials credentials = AwsBasicCredentials.create(
                fileConfig.getAccessKey(),
                fileConfig.getSecretKey()
        );
        StaticCredentialsProvider credentialsProvider = StaticCredentialsProvider.create(credentials);

        // 创建 S3 客户端
        this.s3Client = S3AsyncClient.builder()
                .credentialsProvider(credentialsProvider)
                .endpointOverride(URI.create(getEndpoint()))
                .region(Region.of(getRegion()))
                .httpClient(NettyNioAsyncHttpClient.builder()
                        .connectionTimeout(Duration.ofSeconds(60))
                        .build()
                )
                .build();

        // 创建 S3 传输管理器
        this.s3TransferManager = S3TransferManager.builder().s3Client(s3Client).build();

        // 创建 S3 配置
        S3Configuration s3Configuration = S3Configuration.builder()
                .chunkedEncodingEnabled(false)
                .build();

        // 创建预签名URL生成器
        this.s3Presigner = S3Presigner.builder()
                .credentialsProvider(credentialsProvider)
                .region(Region.of(getRegion()))
                .endpointOverride(URI.create(getEndpoint()))
                .serviceConfiguration(s3Configuration)
                .build();

    }


    /**
     * 上传
     *
     * @param inputStream   文件输入流
     * @param contentLength 文件长度
     * @param filename      文件名
     * @param contentType   文件类型
     * @return 上传结构
     */
    public UploadResult upload(InputStream inputStream, Integer contentLength, String filename, String contentType) {
        try {
            // 创建请求体
            BlockingInputStreamAsyncRequestBody body = BlockingInputStreamAsyncRequestBody.builder()
                    .contentLength(Long.valueOf(contentLength))
                    .subscribeTimeout(Duration.ofSeconds(30))
                    .build();

            // 创建上传请求
            Upload upload = this.s3TransferManager.upload(request -> {
                        request.requestBody(body)
                                .putObjectRequest(r -> {
                                    r.bucket(fileConfig.getBucketName())
                                            .key(filename)
                                            .contentType(contentType)
                                            .build();
                                }).build();
                    }
            );

            // 将输入流写入请求体
            body.writeInputStream(inputStream);

            // 等待文件上传
            CompletedUpload result = upload.completionFuture().join();

            // 构建上传结果
            return UploadResult.builder()
                    .filename(filename)
                    .url(getUrl() + "/" + filename)
                    .eTag(result.response().eTag())
                    .build();
        } catch (Exception e) {
            e.printStackTrace();
            throw new CustomException(ResultCodeEnum.FILE_UPLOAD_ERROR);
        }
    }


    /**
     * 获取私有文件 URL
     *
     * @param filename       文件名
     * @param expirationTime 过期时间
     * @return 文件 URL
     */
    public String getPrivateUrl(String filename, Duration expirationTime) {
        return s3Presigner.presignGetObject(request -> {
                    request.signatureDuration(expirationTime)
                            .getObjectRequest(r -> r.bucket(fileConfig.getBucketName()).key(filename).build())
                            .build();
                })
                .url()
                .toString();
    }

    /**
     * 下载文件
     *
     * @param filename 文件名
     */
    public Download<ResponseInputStream<GetObjectResponse>> download(String filename) {
        try {
            GetObjectRequest getObjectRequest = GetObjectRequest.builder()
                    .bucket(fileConfig.getBucketName())
                    .key(filename)
                    .build();

            // 创建下载请求
            DownloadRequest<ResponseInputStream<GetObjectResponse>> downloadRequest = DownloadRequest.builder()
                    .getObjectRequest(getObjectRequest)
                    .addTransferListener(LoggingTransferListener.create())
                    .responseTransformer(AsyncResponseTransformer.toBlockingInputStream())
                    .build();

            return s3TransferManager.download(downloadRequest);
        } catch (Exception e) {
            throw new CustomException(ResultCodeEnum.FILE_DOWNLOAD_ERROR);
        }
    }

    /**
     * 删除文件
     *
     * @param filename 文件名
     */
    public void delete(String filename) {
        s3Client.deleteObject(d -> d.bucket(fileConfig.getBucketName())
                .key(filename)
                .build()
        );
    }


    /**
     * 获取文件存储地址
     *
     * @return 文件存储地址
     */
    private String getEndpoint() {
        if ("MINIO".equalsIgnoreCase(fileConfig.getStorageType())) {
            return fileConfig.getEndpoint();
        }

        return "https://" + fileConfig.getEndpoint();
    }

    private boolean getStyle() {
        return "MINIO".equals(fileConfig.getStorageType());
    }

    /**
     * 获取文件存储区域
     *
     * @return 文件存储区域
     */
    private String getRegion() {
        if (fileConfig.getRegion().isEmpty()) {
            return "us-east-1";
        } else {
            return fileConfig.getRegion();
        }
    }

    /**
     * 获取文件 URL
     *
     * @return 文件 URL
     */
    private String getUrl() {
        String endpoint = fileConfig.getEndpoint();
        String bucketName = fileConfig.getBucketName();

        // minio 单独处理
        if ("MINIO".equals(fileConfig.getStorageType())) {
            return endpoint + "/" + bucketName;
        }

        return "https://" + bucketName + "." + endpoint;
    }

}
